﻿using System;
using System.Collections.Generic;

namespace TopHero
{
    public static class PropertyId
    {
        public const int HP = 1;
        public const int HP_RATE = 2;
        public const int MAX_HP = 3;
        public const int LOOP_NUM = 4;
        public const int LIGHT_TIME = 5;
        public const int MAX_LIGHT_TIME = 6;
        public const int LIGHT_TIME_RATE = 7;
        public const int ATK = 8;
        public const int SPEED = 9;
        public const int MAX_SPEED = 10;
        public const int SPEED_RATE = 11;
    }

    public class Property
    {
        public Property(int propertyId)
        {
            this.propertyId = propertyId;
        }

        public int propertyId { get; }

        public float value;
    }

    public abstract class UpgradeProperty : Property
    {
        public Property rate;
        public Property limit;

        public abstract void Update();

        public UpgradeProperty(int propertyId) : base(propertyId)
        {
        }
    }

    public class LinerProperty : UpgradeProperty
    {
        public override void Update()
        {
            if (rate == null)
            {
                return;
            }

            value += rate.value * TickCounter.Instance.deltaTime;
        }

        public LinerProperty(int propertyId) : base(propertyId)
        {
        }
    }

    public class PercentProperty : UpgradeProperty
    {
        public Property baseRate;

        public override void Update()
        {
            Debug.Assert(rate != null && baseRate != null && limit != null);
            float add = baseRate.value * (rate.value * TickCounter.Instance.deltaTime);
            value += add;

            value = Math.Min(value, limit.value);
        }

        public PercentProperty(int propertyId) : base(propertyId)
        {
        }
    }

    public class PropertyManager
    {
        private Dictionary<int, Property> _properties;

        public PropertyManager()
        {
            _properties = new Dictionary<int, Property>();
        }

        public Property AddProperty(int id, float value)
        {
            Debug.Assert(!_properties.ContainsKey(id));
            Property property = new Property(id);
            property.value = value;
            _properties.Add(id, property);
            return property;
        }

        public Property AddLinerProperty(int id, float value, Property rate, Property limit)
        {
            Debug.Assert(!_properties.ContainsKey(id));
            LinerProperty property = new LinerProperty(id);
            property.value = value;
            property.rate = rate;
            _properties.Add(id, property);
            return property;
        }

        public Property AddPercentProperty(int id, float value, Property rate)
        {
            Debug.Assert(!_properties.ContainsKey(id));
            PercentProperty property = new PercentProperty(id);
            property.value = value;
            property.rate = rate;
            property.baseRate = property;
            _properties.Add(id, property);
            return property;
        }

        public Property AddBasePercentProperty(int id, float value, Property baseRate, Property rate, Property limit)
        {
            Debug.Assert(!_properties.ContainsKey(id));
            PercentProperty property = new PercentProperty(id);
            property.value = value;
            property.rate = rate;
            property.limit = limit;
            property.baseRate = baseRate;
            _properties.Add(id, property);
            return property;
        }

        public Property GetProperty(int id)
        {
            Debug.Assert(_properties.ContainsKey(id));
            return _properties[id];
        }

        public float GetValue(int id)
        {
            Debug.Assert(_properties.ContainsKey(id));
            return _properties[id].value;
        }

        public void Update()
        {
            foreach (var v in _properties)
            {
                Property property = v.Value;
                if (property is UpgradeProperty)
                {
                    ((UpgradeProperty) property).Update();
                }
            }
        }
    }
}